﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MetaModelType.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the MetaModelType type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.ComponentModel.Modelling
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using Reflection;

    public class MetaModelType : MetaModelMember
    {
        #region Fields

        private MetaModelMemberCollection<MetaModelProperty> _properties;
        private readonly MetaModel _model;

        #endregion

        internal MetaModelType(MetaModel model, Type type)
        {
            _model = model;
            ReflectedType = type;
            if (type.BaseType != null)
            {
                BaseType = model.GetType(type.BaseType);
            }
        }

        public MetaModelType BaseType { get; private set; }

        public MetaModelMemberCollection<MetaModelProperty> Properties
        {
            get
            {
                if (_properties == null)
                {
                    Interlocked.CompareExchange(ref _properties, CreateProperties(), null);
                }

                return _properties;
            }
        }

        private MetaModelMemberCollection<MetaModelProperty> CreateProperties()
        {
            return new MetaModelMemberCollection<MetaModelProperty>(
                GetUniqueProperties()
                .Select(propertyInfo => 
                    new MetaModelProperty(this, 
                        propertyInfo.PropertyInfo, 
                        propertyInfo.Index))
                .ToList());
        }

        private IEnumerable<PropertyInfoWithIndex> GetUniqueProperties()
        {
            var matchedProperties = new List<string>();
            var properties = ReflectedType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            var i = 0;

            foreach (var property in properties)
            {
                var skip = properties
                    .Where(potentialDuplicate => potentialDuplicate.Name == property.Name)
                    .Any(potentialDuplicate => property.GetDepthInInheritanceTree() < potentialDuplicate.GetDepthInInheritanceTree());

                if (!skip)
                {
                    // there is still a chance of getting a duplicate property because it is possible to IL Emit classes
                    // which have two properties at the same depth in the inhieritence with different return types.  Thanks
                    // AutoMock!  This may throw an exception later, TBD
                    if (!matchedProperties.Contains(property.Name))
                    {
                        matchedProperties.Add(property.Name);
                        yield return new PropertyInfoWithIndex(i, property);
                    }
                }
                i++;
            }
        }

        public override MetaModel Model
        {
            get { return _model; }
        }

        public Type ReflectedType { get; private set; }

        public override MemberInfo ReflectedMember
        {
            get { return ReflectedType; }
        }
    }
}